package com.ihr360.commons.lang;

import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;

import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author davidwei
 */
public class Ihr360EntityUtils {
    //private static final Logger logger = LoggerFactory.getLogger(Ihr360EntityUtils.class);

    private Ihr360EntityUtils() {
    }

    public static List<Field> getFieldsWithAnnotation(Object entity, Class annotationClass) {
        final BeanWrapper src = new BeanWrapperImpl(entity);
        Class clazz = src.getWrappedClass();
        clazz = ClassUtils.getUserClass(clazz);
        if (!hasEntityAnnotation(clazz)) {
            return Collections.emptyList();
        }
        Field[] fields = clazz.getDeclaredFields();
        List<Field> retFields = new ArrayList<>();
        for (Field field : fields) {
            if (field.getAnnotation(annotationClass) != null) {
                retFields.add(field);
            }
        }
        return retFields;
    }

    /**
     * 根据Enity中的映射关系，将Entity进行重新封装
     *
     * @param entity
     * @return
     */
    public static void reinitialize(Object entity) {
        final BeanWrapper src = new BeanWrapperImpl(entity);
        Class clazz = src.getWrappedClass();
        clazz = ClassUtils.getUserClass(clazz);
        if (!hasEntityAnnotation(clazz)) {
            return;
        }
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            String fieldName = field.getName();

            if (!src.isReadableProperty(fieldName)) {
                continue;
            }
            if (!src.isWritableProperty(fieldName)) {
                continue;
            }
            Object propertyValue = src.getPropertyValue(fieldName);
            if (propertyValue == null) {
                continue;
            }
            if (isOneToMany(field)) {
                if (propertyValue instanceof Iterable) {
                    List list = ((List) propertyValue);
                    for (Object innerValue : list) {
                        setEntityForProperty(entity, clazz, innerValue);
                    }
                }
            } else if (isAnyToOne(field)) {
                setEntityForProperty(entity, clazz, propertyValue);
            }
        }
    }

    private static boolean isOneToMany(Field field) {
        return field.getAnnotation(OneToMany.class) != null;
    }

    private static boolean isAnyToOne(Field field) {
        return field.getAnnotation(OneToOne.class) != null || field.getAnnotation(ManyToOne.class) != null;
    }

    private static void setEntityForProperty(Object entity, Class clazz, Object propertyValue) {
        final BeanWrapper wrapper = new BeanWrapperImpl(propertyValue);
        PropertyDescriptor[] descriptors = wrapper.getPropertyDescriptors();
        for (PropertyDescriptor desc : descriptors) {
            if (desc.getPropertyType().equals(clazz)) {
                wrapper.setPropertyValue(desc.getName(), entity);
                break;
            }
        }
    }

    private static boolean hasEntityAnnotation(Class clazz) {
        return AnnotationUtils.findAnnotation(clazz, Entity.class) != null;
    }


}